

let target: (null | undefined | Watcher) = null;
let targetStack: Array<Watcher> = []
function pushTarget(watchTarget: Watcher) {
    // 将上一次的 Watcher 存入 targetStack
    targetStack.push(watchTarget)
    target = watchTarget
}

function popTarget() {

    targetStack.pop()
    target = targetStack[targetStack.length - 1]
}

type observeObject<T> = object | Array<T>;

class Observe {
    value: observeObject<any>
    constructor(value: observeObject<any>) {
        this.value = value
        if (Object.prototype.toString.call(this.value).slice(8, -1) === 'Object') {
            this.walk()
        } else {
            //  rewrite Array method

        }
    }
    walk() {
        observe(this.value)
    }
}

function observe(obj: object) {
    if (typeof obj !== 'object') {
        return false
    }

    Object.keys(obj).forEach(key => {
        defineReactive(obj, key)
    })
}

function defineReactive(obj: object, key: string) {
    let val = (obj as any)[key];
    const dep = new Dep()
    Reflect.defineProperty(obj, key, {
        get() {
            dep.depend()
            return val
        },
        set(newVal) {
            if (newVal != val) {

                val = newVal
                dep.notify()
            }
        }
    })
}

class Dep {
    subs: Set<Watcher>;
    constructor() {
        this.subs = new Set()
    }
    //  依赖收集
    depend() {
        if (target) {
            this.subs.add(target)
        }

    }
    //  通知更新
    notify() {
        this.subs.forEach(watcher => watcher.update())
    }
}

interface IWatcher {
    lazy?: boolean;
    watch?: boolean;
    cb?: (value: any, oldVal: any) => void;
}
class Watcher {
    getter: () => void;
    dep?: Dep;
    dirty: boolean;
    lazy: boolean;
    value: any; // 计算结果的值
    watch?: boolean; // 计算结果的值
    cb?: (value: any, oldVal: any) => void; // 计算结果的值
    constructor(getter: () => void, options: IWatcher) {
        this.getter = getter;
        this.lazy = options && (!!options.lazy)
        this.dirty = options && !!(options.lazy)
        this.watch = options && !!(options.watch)
        this.cb = options && (options.cb)
        //  表示计算属性computed 惰性求值
        if (this.lazy) {
            this.dep = new Dep()
        } else {
            this.value = this.get()
        }
    }
    get() {
        // 收集依赖
        pushTarget(this)
        //  作为computed 的返回值
        let value = this.getter()
        popTarget()
        return value
    }

    depend() {
        this.dep?.depend()
    }
    update() {
        if (this.lazy) {
            //  当computed更新之后，会 调用notify 来触发computedWatcher 的 update 此时将dirty设置为true，即代表当前的computed的值为脏值 需要重新计算
            this.dirty = true
        } else if (this.watch) {
            const oldVal = this.value;
            this.value = this.get()
            if (oldVal != this.value) {
                this.cb && this.cb(this.value, oldVal)
            }
        } else {
            this.get()
        }
    }
    evaluate() {
        this.value = this.get()
        this.dirty = false
        //  当重新计算computed的值之后 将dirty重置为false ，并通知依赖当前computed的watcher更新
        this.dep?.notify()

    }
}


interface IComputedResult<T> {
    value: T
}

const computedWatcherOption = { lazy: true }
function computed<T>(fn: () => any): IComputedResult<T> {
    let def: any = {}

    const computedWatcher = new Watcher(fn, computedWatcherOption);

    Reflect.defineProperty(def, 'value', {
        get() {
            if (computedWatcher.dirty) {
                computedWatcher.evaluate()
            }
            if (target) {
                computedWatcher.depend()
            }
            return computedWatcher.value
        }
    })
    return def
}

const watchWatcherOption = { watch: true }
function watch<T>(getter: () => void, cb: (value: T, oldVal: T) => void) {
    const watchWatcher = new Watcher(getter, { ...watchWatcherOption, cb })
}

//  computed



